package cc.mrbird.febs.statistics.service.impl;

import cc.mrbird.febs.accounting.dto.standardSub.StandardSubDTO;
import cc.mrbird.febs.accounting.entity.*;
import cc.mrbird.febs.accounting.mapper.CompanyProjectMapper;
import cc.mrbird.febs.accounting.mapper.StandardSubExtraMapper;
import cc.mrbird.febs.accounting.service.*;
import cc.mrbird.febs.common.entity.QueryRequest;
import cc.mrbird.febs.common.utils.FebsUtil;
import cc.mrbird.febs.statistics.dto.QueryParam;
import cc.mrbird.febs.statistics.service.IOverviewService;
import cc.mrbird.febs.utils.*;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageInfo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class OverviewServiceImpl implements IOverviewService {

    @Resource
    private ICompanyService companyService;
    @Resource
    private IProjectService projectService;
    @Resource
    private IDetailCostService detailCostService;
    @Resource
    private IStandardSubService standardSubService;
    @Resource
    private IStandardSubExtraService standardSubExtraService;
    @Resource
    private StandardSubExtraMapper standardSubExtraMapper;
    @Resource
    private CompanyProjectMapper companyProjectMapper;

    @Override
    public PagingDTO overviewTable(QueryRequest request, QueryParam queryParam) {
        List<JSONObject> res = new ArrayList<>();
        List<String> projectIds_uni = new ArrayList<>();
        List<String> standardIds = Arrays.asList(queryParam.getStandardId().split(","));
        List<String> yearMonths = new ArrayList<>();
        List<String> months = new ArrayList<>(Arrays.asList(queryParam.getMonth().split(",")));
        for (int i = 1; i <= 12; i++) {
            if (months.contains(i + "")) {
                yearMonths.add(queryParam.getYear().concat("-").concat(i < 10 ? "0" + i : i + ""));
            }
        }
        List<Project> projects = projectService.findProjects(null);
        Map<Long, Project> projectMap = projects.stream().collect(Collectors.toMap(Project::getId, Function.identity()));
        LambdaQueryWrapper<Company> queryWrapper1 = new LambdaQueryWrapper<>();
        queryWrapper1.eq(Company::getIsDeleted, 0);
        List<Company> companies = companyService.getBaseMapper().selectList(queryWrapper1);
        Map<Long, Company> companyMap = companies.stream().collect(Collectors.toMap(Company::getId, Function.identity()));
        // 1.按公司查询 2.按项目查询, 和前端对应
        if (queryParam.getQueryBy().equals("1")) {
            List<String> companyIds = Arrays.asList(queryParam.getCompanyId().split(","));
            LambdaQueryWrapper<CompanyProject> q = new LambdaQueryWrapper<>();
            q.in(CompanyProject::getCompanyId, companyIds).select(CompanyProject::getProjectId);
            List<CompanyProject> companyProjectList = companyProjectMapper.selectList(q);
            for (CompanyProject companyProject : companyProjectList) {
                projectIds_uni.add(companyProject.getProjectId().toString());
            }
        } else {
            projectIds_uni = Arrays.asList(queryParam.getProjectId().split(","));
        }
        List<String> projectIds = new ArrayList<>(projectIds_uni);
        // 获取所有标准科目
        List<StandardSub> standardSubs = standardSubService.queryByType(null);
        Map<String, StandardSub> codeStandards = new HashMap<>();
        for (StandardSub standardSub : standardSubs) {
            codeStandards.put(standardSub.getCode(), standardSub);
        }
        // 获取额外科目
        List<StandardSubExtra> standardSubExtras = standardSubExtraMapper.getByProjectId(projectIds);
        for (StandardSubExtra standardSubExtra : standardSubExtras) {
            if (standardSubExtra == null) continue;
            StandardSub standardSub = new StandardSub();
            standardSub.setId(standardSubExtra.getId() + 1000);
            standardSub.setCode(standardSubExtra.getCode());
            standardSub.setName(standardSubExtra.getName());
            codeStandards.put(standardSub.getCode(), standardSub);
        }
        // 获取所有选择的项目和年月对应的记录(区别预估项)
        String ifEstimate = queryParam.getIfEstimate();
        List<DetailCost> detailCosts = detailCostService.queryByProjectsAndYearMonths(projectIds, yearMonths, ifEstimate);
        Map<String, Double> valueMap = new HashMap<>(); // key:项目id-年月-明细Id
        for (DetailCost detailCost : detailCosts) {
            String key = detailCost.getProjectId().toString() + "-" + detailCost.getYearMonth() + "-"
                    + codeStandards.get(detailCost.getDetailSubjectCode()).getId();
            valueMap.put(key, detailCost.getCost().doubleValue());
        }
        // 获取选择的第三类（计算）科目
        List<StandardSub> calculateStandards = standardSubs.stream()
                .filter(standardSub -> standardSub.getType() == (byte) 3)
                .sorted(Comparator.comparing(StandardSub::getLevle)).collect(Collectors.toList());
        // 计算第三类科目
        ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
        for (String projectId : projectIds) {
            for (StandardSub standardSub : calculateStandards) {
                String rule = standardSub.getCalculateRule();
                Set<String> ruleCodeSet = CodeUtils.getRuleCodeSet(rule);
                for (String yearMonth : yearMonths) {
                    Double result = 0d;
                    // 判断类别，为 4 ,5 类直接累加计算, 否则通过存入公式计算
                    if (standardSub.getCode().equals(CodeConstant.thirdPartCode)) {
                        for (DetailCost e : detailCosts) {
                            if (e.getProjectId().toString().equals(projectId) && e.getYearMonth().equals(yearMonth) && e.getType() == 4) {
                                result += e.getCost().doubleValue();
                            }
                        }
                    } else if (standardSub.getCode().equals(CodeConstant.selfIncomeCode)) {
                        for (DetailCost e : detailCosts) {
                            if (e.getProjectId().toString().equals(projectId) && e.getYearMonth().equals(yearMonth) && e.getType() == 5) {
                                result += e.getCost().doubleValue();
                            }
                        }
                    } else if (standardSub.getCode().equals(CodeConstant.thirdCostSumCode)) {
                        for (DetailCost e : detailCosts) {
                            if (e.getProjectId().toString().equals(projectId) && e.getYearMonth().equals(yearMonth)
                                    && (e.getType() == 6 || e.getDetailSubjectCode().equals(CodeConstant.pendingPaymentCode))) {
                                result += e.getCost().doubleValue();
                            }
                        }
                    } else if (standardSub.getCode().equals(CodeConstant.projectIncomeCode)) {
                        // 加上自有收入合计和第三方收入合计
                        String key = projectId + "-" + yearMonth + "-" + codeStandards.get(CodeConstant.selfIncomeCode).getId();
                        result += valueMap.getOrDefault(key, 0d);
                        key = projectId + "-" + yearMonth + "-" + codeStandards.get(CodeConstant.thirdPartCode).getId();
                        result += valueMap.getOrDefault(key, 0d);
                    } else {
                        String realRule = rule;
                        // 通过公式计算
                        for (String ruleCode : ruleCodeSet) {
                            String key = projectId + "-" + yearMonth + "-" + codeStandards.get(ruleCode).getId();
                            Double value = valueMap.getOrDefault(key, 0d);
                            realRule = realRule.replaceAll("【" + ruleCode + "】", value.toString());
                        }
                        //计算公式
                        try {
                            result = (Double) jse.eval(realRule);
                        } catch (ScriptException e) {
                            e.printStackTrace();
                        }
                        if (result.isNaN() || result.isInfinite()) {
                            result = 0d;
                        }
                    }
                    result = Double.parseDouble(String.format("%.2f", result));
                    String key = projectId + "-" + yearMonth + "-" + codeStandards.get(standardSub.getCode()).getId();
                    valueMap.put(key, result);
                }
            }
        }
        // 将结果按项目合并
        Map<String, Double> resultValue = new HashMap<>();
        for (String projectId : projectIds) {
            for (String standardId : standardIds) {
                for (String yearMonth : yearMonths) {
                    String costKey = projectId + "-" + yearMonth + "-" + standardId;
                    String key = projectId + "-" + standardId;
                    resultValue.put(key, ArithUtil.add(resultValue.getOrDefault(key, 0d), valueMap.getOrDefault(costKey, 0d)));
                }
            }
        }
        // 列索引
        String[] cols = new String[1000];
        String[] mons = new String[1000];
        for (int i = 0; i < 1000; i++) {
            cols[i] = "col" + (i + 1);
            mons[i] = "mon" + (i + 1);
        }
        Map<String, Double> companyMonthMap = new HashMap<>();  // 公司科目按月份合并后结果
        if (queryParam.getQueryBy().equals("2")) {
            for (String projectId : projectIds) {
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("title", projectMap.get(Long.parseLong(projectId)).getProjectName());
                for (int i = 0; i < standardIds.size(); i++) {
                    String key = projectId + "-" + standardIds.get(i);
                    jsonObject.put(cols[i], resultValue.get(key));
                }
                // 添加月份详情
                if (queryParam.getIfShowMonth().equals("1")) {
                    for (int i = 0; i < standardIds.size(); i++) {
                        for (int j = 0; j < yearMonths.size(); j++) {
                            String costKey = projectId + "-" + yearMonths.get(j) + "-" + standardIds.get(i);
                            jsonObject.put(mons[yearMonths.size() * i + j], valueMap.getOrDefault(costKey, 0d));
                        }
                    }
                }
                res.add(jsonObject);
            }
        } else {
            // 按公司查询，先按公司合并统计金额
            String[] companyIds = queryParam.getCompanyId().split(",");
            Map<String, Double> companyValue = new HashMap<>();
            for (String companyId : companyIds) {
                List<Project> queryProjects = projects.stream()
                        .filter(project -> companyId.equals(project.getCompanyId().toString()))
                        .collect(Collectors.toList());
                for (String standardId : standardIds) {
                    for (Project queryProject : queryProjects) {
                        String costKey = queryProject.getId() + "-" + standardId;
                        String key = companyId + "-" + standardId;
                        companyValue.put(key, ArithUtil.add(companyValue.getOrDefault(key, 0d), resultValue.getOrDefault(costKey, 0d)));
                        // 添加月份详情
                        if (queryParam.getIfShowMonth().equals("1")) {
                            for (String yearMonth : yearMonths) {
                                String firstKey = queryProject.getId() + "-" + yearMonth + "-" + standardId;
                                String monthKey = companyId + "-" + yearMonth + "-" + standardId;
                                companyMonthMap.put(monthKey, ArithUtil.add(companyMonthMap.getOrDefault(monthKey, 0d), valueMap.getOrDefault(firstKey, 0d)));
                            }
                        }
                    }
                }
            }
            for (String companyId : companyIds) {
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("title", companyMap.get(Long.parseLong(companyId)).getCompanyName());
                for (int i = 0; i < standardIds.size(); i++) {
                    String key = companyId + "-" + standardIds.get(i);
                    jsonObject.put(cols[i], companyValue.getOrDefault(key, 0d));
                }
                // 添加月份详情
                if (queryParam.getIfShowMonth().equals("1")) {
                    for (int i = 0; i < standardIds.size(); i++) {
                        for (int j = 0; j < yearMonths.size(); j++) {
                            String key = companyId + "-" + yearMonths.get(j) + "-" + standardIds.get(i);
                            jsonObject.put(mons[yearMonths.size() * i + j], companyMonthMap.getOrDefault(key, 0d));
                        }
                    }
                }
                res.add(jsonObject);
            }
        }
        // 模糊查询
        if (StringUtils.isNotBlank(queryParam.getTitle())) {
            res = res.stream().filter(r -> r.getString("title").contains(queryParam.getTitle())).collect(Collectors.toList());
        }

        // 排序
        if (StringUtils.isNotBlank(request.getField())) {
            String order = request.getOrder();
            String filed = request.getField();
            if (order.equals("asc")) {
                res.sort(Comparator.comparing(o -> o.getDouble(filed)));
            } else {
                res.sort((o1, o2) -> o2.getDouble(filed).compareTo(o1.getDouble(filed)));
            }
        } else {    // 默认按第一个科目降序排序
            res.sort((o1, o2) -> o2.getDouble("col1").compareTo(o1.getDouble("col1")));
        }

        // 添加合计行
        JSONObject total = new JSONObject();
        total.put("title", "合计");
        for (int i = 0; i < standardIds.size(); i++) {
            double totalCost = 0d;
            for (JSONObject r : res) {
                totalCost = ArithUtil.add(totalCost, r.getDouble(cols[i]));
            }
            total.put(cols[i], totalCost);
            if (queryParam.getIfShowMonth().equals("1")) {
                for (int j = 0; j < months.size(); j++) {
                    double totalMonthCost = 0d;
                    for (JSONObject r : res) {
                        totalMonthCost = ArithUtil.add(totalMonthCost, r.getDouble(mons[i * months.size() + j]));
                    }
                    total.put(mons[i * months.size() + j], totalMonthCost);
                }
            }
        }
        res.add(0, total);
        // 添加序号
        for (int i = 0; i < res.size(); i++) {
            res.get(i).put("key", i + 1);
        }
        // 手动分页
        int start = (request.getPageNum() - 1) * request.getPageSize();
        Stream<JSONObject> stream = res.stream().skip(start).limit(request.getPageSize());
        List<JSONObject> data = stream.collect(Collectors.toList());

        return new PagingDTO(
                request.getPageNum(),
                request.getPageSize(),
                new PageInfo<>(res).getTotal(),
                data);
    }

    @Override
    public PagingDTO detailTable(QueryRequest request, QueryParam queryParam) {
        Set<String> projectIds_uni = new HashSet<>();
        List<String> yearMonths = new ArrayList<>();
        List<String> months = new ArrayList<>(Arrays.asList(queryParam.getMonth().split(",")));
        List<String> pMonths = new ArrayList<>();
        for (int i = 1; i <= 12; i++) {
            if (months.contains(i + "")) {
                yearMonths.add(queryParam.getYear().concat("-").concat(i < 10 ? "0" + i : i + ""));
                pMonths.add(i + "");
            }
        }
        List<Project> projects = projectService.findProjects(null);
        Map<Long, Project> projectMap = projects.stream().collect(Collectors.toMap(Project::getId, Function.identity()));
        // 1.按公司查询 2.按项目查询, 和前端对应
        if (queryParam.getQueryBy().equals("1")) {
            List<String> companyIds = Arrays.asList(queryParam.getCompanyId().split(","));
            LambdaQueryWrapper<CompanyProject> q = new LambdaQueryWrapper<>();
            q.in(CompanyProject::getCompanyId, companyIds).select(CompanyProject::getProjectId);
            List<CompanyProject> companyProjectList = companyProjectMapper.selectList(q);
            for (CompanyProject companyProject : companyProjectList) {
                projectIds_uni.add(companyProject.getProjectId().toString());
            }
        } else {
            projectIds_uni = new HashSet<>(Arrays.asList(queryParam.getProjectId().split(",")));
        }
        List<String> projectIds = new ArrayList<>(projectIds_uni);
        // 获取所有查询科目大类对应明细科目
        String[] index = {"", "收入", "费用", "成本", "税费"};      //查询科目大类，和前端对应
        String mainName = index[Integer.parseInt(queryParam.getDetailedStandard())];
        List<StandardSubDTO> standardSubDTOS = standardSubService.getDetailStatisticsStandard(projectIds, mainName);
        // 获取所有选择的项目和年月对应的记录(区别预估项)
        String ifEstimate = queryParam.getIfEstimate();
        List<DetailCost> detailCosts = detailCostService.queryByProjectsAndYearMonths(projectIds, yearMonths, ifEstimate);
        Map<String, Double> valueMap = new HashMap<>(); // key:项目id-年月-明细Id
        for (DetailCost detailCost : detailCosts) {
            String key = detailCost.getProjectId().toString() + "-" + detailCost.getYearMonth() + "-"
                    + detailCost.getDetailSubjectCode();
            valueMap.put(key, detailCost.getCost().doubleValue());
        }
        // 获取选择的第三类（计算）科目
        List<StandardSubDTO> calculateStandards = standardSubDTOS.stream()
                .filter(standardSub -> standardSub.getType() == 3)
                .sorted(Comparator.comparing(StandardSubDTO::getLevel)).collect(Collectors.toList());
        // 计算第三类科目
        ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
        for (String projectId : projectIds) {
            for (StandardSubDTO standardSub : calculateStandards) {
                String rule = standardSub.getCalculateRule();
                Set<String> ruleCodeSet = CodeUtils.getRuleCodeSet(rule);
                for (String yearMonth : yearMonths) {
                    Double result = 0d;
                    // 判断类别，为 4 ,5 类直接累加计算, 否则通过存入公式计算
                    if (standardSub.getSubCode().equals(CodeConstant.thirdPartCode)) {
                        for (DetailCost e : detailCosts) {
                            if (e.getProjectId().toString().equals(projectId) && e.getYearMonth().equals(yearMonth) && e.getType() == 4) {
                                result += e.getCost().doubleValue();
                            }
                        }
                    } else if (standardSub.getSubCode().equals(CodeConstant.selfIncomeCode)) {
                        for (DetailCost e : detailCosts) {
                            if (e.getProjectId().toString().equals(projectId) && e.getYearMonth().equals(yearMonth) && e.getType() == 5) {
                                result += e.getCost().doubleValue();
                            }
                        }
                    } else if (standardSub.getSubCode().equals(CodeConstant.thirdCostSumCode)) {
                        for (DetailCost e : detailCosts) {
                            if (e.getProjectId().toString().equals(projectId) && e.getYearMonth().equals(yearMonth)
                                    && (e.getType() == 6 || e.getDetailSubjectCode().equals(CodeConstant.pendingPaymentCode))) {
                                result += e.getCost().doubleValue();
                            }
                        }
                    } else if (standardSub.getSubCode().equals(CodeConstant.projectIncomeCode)) {
                        // 加上自有收入合计和第三方收入合计
                        String key = projectId + "-" + yearMonth + "-" + CodeConstant.selfIncomeCode;
                        result += valueMap.getOrDefault(key, 0d);
                        key = projectId + "-" + yearMonth + "-" + CodeConstant.thirdPartCode;
                        result += valueMap.getOrDefault(key, 0d);
                    } else {
                        String realRule = rule;
                        // 通过公式计算
                        for (String ruleCode : ruleCodeSet) {
                            String key = projectId + "-" + yearMonth + "-" + ruleCode;
                            Double value = valueMap.getOrDefault(key, 0d);
                            realRule = realRule.replaceAll("【" + ruleCode + "】", value.toString());
                        }
                        //计算公式
                        try {
                            result = (Double) jse.eval(realRule);
                        } catch (ScriptException e) {
                            e.printStackTrace();
                        }
                        if (result.isNaN() || result.isInfinite()) {
                            result = 0d;
                        }
                    }
                    result = Double.parseDouble(String.format("%.2f", result));
                    String key = projectId + "-" + yearMonth + "-" + standardSub.getSubCode();
                    valueMap.put(key, result);
                }
            }
        }
        // 将结果按科目合并
        List<String> specialTypes = Arrays.asList("4", "5", "6");  // 4 收入第三方 5 自有收入 6 第三方支出 添加前缀
        List<String> pres = Arrays.asList("收入第三方", "自有收入", "第三方支出");
        Map<String, Double> monthValue = new HashMap<>();
        for (StandardSubDTO standardSubDTO : standardSubDTOS) {
            standardSubDTO.setCost(0d);
            for (String projectId : projectIds) {
                for (String yearMonth : yearMonths) {
                    String monthKey = yearMonth + "-" + standardSubDTO.getSubCode();
                    String costKey = projectId + "-" + monthKey;
                    standardSubDTO.setCost(ArithUtil.add(standardSubDTO.getCost(), valueMap.getOrDefault(costKey, 0d)));
                    if (specialTypes.contains(standardSubDTO.getType().toString()) && Objects.isNull(standardSubDTO.getHasRename())
                            && valueMap.containsKey(costKey)) {
                        standardSubDTO.setSubName(standardSubDTO.getSubName() + "(" + pres.get(standardSubDTO.getType() - 4)
                                + "-" + projectMap.get(Long.parseLong(projectId)).getProjectName() + ")");
                        standardSubDTO.setHasRename(true);
                    }
                    monthValue.put(monthKey, ArithUtil.add(monthValue.getOrDefault(monthKey, 0d), valueMap.getOrDefault(costKey, 0d)));
                }
            }
        }
        List<JSONObject> res = new ArrayList<>();
        for (StandardSubDTO standardSubDTO : standardSubDTOS) {
            JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(standardSubDTO));
            if (queryParam.getIfShowMonth().equals("1")) {
                for (int i = 0; i < yearMonths.size(); i++) {
                    String key = yearMonths.get(i) + "-" + jsonObject.getString("subCode");
                    jsonObject.put(pMonths.get(i), monthValue.getOrDefault(key, 0d));
                }
            }
            res.add(jsonObject);
        }
        // 去除自定义科目金额为0项
        res = res.stream().filter(e -> !specialTypes.contains(e.getString("type")) || e.getDouble("cost") != 0d)
                .collect(Collectors.toList());
        // 模糊查询
        if (StringUtils.isNotBlank(queryParam.getTitle())) {
            res = res.stream().filter(e -> e.getString("subName").contains(queryParam.getTitle())).collect(Collectors.toList());
        }
        for (int i = 0; i < res.size(); i++) {
            res.get(i).put("key", i + 1);
        }

        // 手动分页
        int start = (request.getPageNum() - 1) * request.getPageSize();
        Stream<JSONObject> stream = res.stream().skip(start).limit(request.getPageSize());
        List<JSONObject> data = stream.collect(Collectors.toList());
        return new PagingDTO(
                request.getPageNum(),
                request.getPageSize(),
                new PageInfo<>(res).getTotal(),
                data);
    }
}
